﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Office.Interop.Visio;
using GeoVision.Addin.Core.Spatial;
using GeoVision.Addin.Core.Exceptions;
using System.Windows;
using System.Windows.Media;
using GeoVision.Addin.Core.DataManagement;


namespace GeoVision.Addin.Core
{
    public abstract class GeometryVisioGenerator
    {
        private System.Windows.Forms.Form _waitWindow;
        private Page _page;
        protected VisShapeCreationInfo creationInfo;
        private double canvasWidth;
        private double canvasHeight;
        private double leftMargin;
        private double headerMargin;
        private double canvasRatio;



        public System.Windows.Forms.Form WaitWindow
        {
            get { return _waitWindow; }
            set { _waitWindow = value; }
        }

        protected GeometryVisioGenerator(Page page, VisShapeCreationInfo creationInfo)
        {
            this._page = page;
            this.creationInfo = creationInfo;
            leftMargin = ActiveVisioPage.Document.LeftMargin[VisUnitCodes.visUnitsPolyline];
            headerMargin = ActiveVisioPage.Document.HeaderMargin[VisUnitCodes.visUnitsPolyline];
            canvasWidth = ActiveVisioPage.Document.PaperWidth[VisUnitCodes.visUnitsPolyline] - leftMargin - ActiveVisioPage.Document.RightMargin[VisUnitCodes.visUnitsPolyline];
            canvasHeight = ActiveVisioPage.Document.PaperHeight[VisUnitCodes.visUnitsPolyline] - headerMargin - ActiveVisioPage.Document.FooterMargin[VisUnitCodes.visUnitsPolyline];
            canvasRatio = canvasWidth / canvasHeight;
        }


        public Page ActiveVisioPage
        {
            get { return _page; }
            set { _page = value; }
        }

        private double[] TransformGeoPointToCanvas(double xMax, double yMax, double xMin, double yMin, GeoPoint value)
        {

            double[] result = new double[2];
            double width = Math.Abs(xMax - xMin);
            double height = Math.Abs(yMax - yMin);
            double aspectRatio = width / height;

            double scaleFactor = 1.0;

            if (aspectRatio < canvasRatio)
                scaleFactor = canvasHeight / height;
            else
                scaleFactor = canvasWidth / width;

            double xformTransX = (canvasWidth - (xMin + xMax) * scaleFactor) / 2 + leftMargin;
            double xformTransY = (canvasHeight + (yMin + yMax) * -scaleFactor) / 2 + headerMargin;

            result[0] = Globals.ThisAddIn.Application.ConvertResult(value.Long * scaleFactor + xformTransX, VisUnitCodes.visUnitsPolyline, VisUnitCodes.visInches);
            result[1] = Globals.ThisAddIn.Application.ConvertResult(value.Lat * (scaleFactor) + xformTransY, VisUnitCodes.visUnitsPolyline, VisUnitCodes.visInches);

            return result;
        }

        private double[] TransformGeoPointToCanvas(GeoShapeList list, GeoPoint value)
        {
            double xMax = list.XMax;
            double yMax = list.YMax;
            double xMin = list.XMin;
            double yMin = list.YMin;
            return TransformGeoPointToCanvas(xMax, yMax, xMin, yMin, value);
        }

        public abstract void Draw();


        public virtual void DrawPolygonsToVisioDoc(GeoShapeList shapes)
        {
            double xMax = shapes.XMax;
            double yMax = shapes.YMax;
            double xMin = shapes.XMin;
            double yMin = shapes.YMin;

            DrawPolygonsToVisioDoc(xMax, yMax, xMin, yMin, shapes);
        }

        public virtual void DrawPolygonsToVisioDoc(double xMax, double yMax, double xMin, double yMin, GeoShapeList shapes)
        {
            DrawPolygonsToVisioDoc(xMax, yMax, xMin, yMin, shapes, true);
            if (creationInfo.CreateNewPages)
            {
                foreach (var shape in shapes)
                {
                    ActiveVisioPage = ActiveVisioPage.Document.Pages.Add();
                    if (!string.IsNullOrEmpty(creationInfo.PagenameField) && shape.Data != null)
                        ActiveVisioPage.Name = System.Web.UI.DataBinder.Eval(shape.Data, creationInfo.PagenameField, "{0}");

                    GeoShapeList shapeList = new GeoShapeList();
                    shapeList.Add(shape);
                    DrawPolygonsToVisioDoc(shapeList.XMax, shapeList.YMax, shapeList.XMin, shapeList.YMin, shapeList, false);
                }

            }
        }

        public virtual void DrawPolygonsToVisioDoc(double xMax, double yMax, double xMin, double yMin, GeoShapeList shapes, bool firstProcess)
        {

            foreach (GeoShape geoShape in shapes)
            {

                foreach (GeoShapeSegment _segment in geoShape.Segments)
                {
                    List<double> pointList = new List<double>(3000);
                    foreach (GeoPoint _geoPoint in _segment.Points)
                    {
                        double[] result = TransformGeoPointToCanvas(xMax, yMax, xMin, yMin, _geoPoint);
                        pointList.AddRange(result);
                    }
                    double[] visPolygons = RetreiveVisioPolyLineArray(pointList);
                    if (visPolygons.Length != 0)
                        try
                        {
                            Shape visioShape = _page.DrawPolyline(visPolygons, 0);
                            if (firstProcess)
                                geoShape.VisioParentShapeRef = visioShape;
                            FormatAndDatabindShape(visioShape, geoShape, firstProcess);
                        }
                        catch (Exception exc)
                        {
                            MessageBox.Show(exc.Message);
                            pointList.Clear();
                        }
                }
            }

        }

        private void FormatAndDatabindShape(Shape visioShape, GeoShape shape, bool firstProcess)
        {
            if (!creationInfo.Lines)
                visioShape.LineStyle = "none";
            if (!String.IsNullOrEmpty(shape.VisioColor))
                visioShape.CellsSRC[(short)VisSectionIndices.visSectionObject, (short)VisRowIndices.visRowFill, 0].FormulaU = shape.VisioColor;
            else
                if (creationInfo.RandomColors)
                {
                    System.Windows.Media.Color[] colors = new System.Windows.Media.Color[] 
                    {
                        System.Windows.Media.Colors.Crimson, System.Windows.Media.Colors.ForestGreen, System.Windows.Media.Colors.RoyalBlue,
                        System.Windows.Media.Colors.Navy, System.Windows.Media.Colors.DarkSeaGreen, System.Windows.Media.Colors.LightSlateGray, 
                        System.Windows.Media.Colors.DarkKhaki, System.Windows.Media.Colors.Olive, System.Windows.Media.Colors.Indigo, System.Windows.Media.Colors.Violet 
                    };
                    System.Windows.Media.Color randomColor = colors[new Random().Next() % colors.Length];
                    shape.VisioColor = string.Format("RGB({0},{1},{2})", randomColor.R, randomColor.G, randomColor.B);
                    visioShape.CellsSRC[(short)VisSectionIndices.visSectionObject, (short)VisRowIndices.visRowFill, 0].FormulaU = shape.VisioColor;
                }
            dynamic intPropRow2 = 0;
            foreach (Column col in creationInfo.Selectedcolumns)
            {
                int propType = GetPropTypeCode(col);
                if (propType < 0)
                    continue;
                intPropRow2 = visioShape.AddRow((short)VisSectionIndices.visSectionProp, -2, 0);
                Cell cell = visioShape.CellsSRC[(short)VisSectionIndices.visSectionProp, intPropRow2, 2];
                cell.FormulaU = "\"" + col.Name + "\"";
                cell = visioShape.CellsSRC[(short)VisSectionIndices.visSectionProp, intPropRow2, 5];
                cell.FormulaU = propType.ToString();
                cell = visioShape.CellsSRC[(short)VisSectionIndices.visSectionProp, intPropRow2, 0];
                cell.FormulaU = GetPropValue(shape.Data, col);
            }
            if (creationInfo.CreateNewPages && !firstProcess && creationInfo.LinkShapesToPages && shape.VisioParentShapeRef != null)
            {
                Hyperlink link = shape.VisioParentShapeRef.AddHyperlink();
                link.SubAddress = ActiveVisioPage.Name;
            }
            if (creationInfo.AddText && !string.IsNullOrEmpty(creationInfo.TextField) && shape.Data != null)
            {
                visioShape.Characters.Text = System.Web.UI.DataBinder.Eval(shape.Data, creationInfo.TextField, "{0}");
            }
        }

        private string GetPropValue(object container, Column col)
        {
            if (container != null)
            {
                object value = System.Web.UI.DataBinder.Eval(container, col.Name);
                if (value == null)
                    return "";
                switch (col.Type)
                {
                    case DataType.DateTime:
                        return "";
                    case DataType.Decimal:
                    case DataType.Double:
                    case DataType.Int64:
                    case DataType.Integer:
                        {
                            if (value is double)
                                return ((double)value).ToString(System.Globalization.CultureInfo.InvariantCulture);
                            else if (value is float)
                                return ((float)value).ToString(System.Globalization.CultureInfo.InvariantCulture);
                            else if (value is decimal)
                                return ((decimal)value).ToString(System.Globalization.CultureInfo.InvariantCulture);
                            else return value.ToString();
                        }
                    case DataType.String:
                        return "\"" + value.ToString() + "\"";
                    case DataType.Bool:
                        return value.ToString().ToUpperInvariant();
                    case DataType.Currecy:
                        return string.Format("CY({0})", value.ToString());
                    default:
                        return "";
                }
            }
            else return "";

        }

        private int GetPropTypeCode(Column col)
        {
            switch (col.Type)
            {
                case DataType.DateTime:
                    return 5;
                case DataType.Decimal:
                case DataType.Double:
                case DataType.Int64:
                case DataType.Integer:
                    return 2;
                case DataType.String:
                    return 0;
                case DataType.Bool:
                    return 3;
                case DataType.Currecy:
                    return 7;
                default:
                    return -1;
            }
        }

        private double[] RetreiveVisioPolyLineArray(List<double> list)
        {
            if (list.Count > 8000)
            {
                List<double> simplyfyedList = new List<double>();
                int i = 0;
                while (i < list.Count)
                {
                    simplyfyedList.Add(list[i]);
                    simplyfyedList.Add(list[i + 1]);
                    i = i + 4;
                }
                if (simplyfyedList[0] != simplyfyedList[simplyfyedList.Count - 2] &&
                    simplyfyedList[1] != simplyfyedList[simplyfyedList.Count - 1])
                {
                    simplyfyedList.Add(simplyfyedList[0]);
                    simplyfyedList.Add(simplyfyedList[1]);
                }
                var sList = RetreiveVisioPolyLineArray(simplyfyedList);
                return sList;

            }
            else return list.ToArray();
        }
    }
}
